#  Jenkins build script
#  1. use runScript.bat in Simplicity Sutdio to do command line(headless) building
#  2. use BUILD_NAME.txt(for version) and BUILD_ARG.txt(for devices to build)to receive build configuration from jenkins.
#  3. output binary is placed into "output" directory
#  4. also can invoke from command line, for example: python build.py V1.1.0  sub_hub sub_rl
#  5. update the SUB_DEVICE_LIST if new sub-device type added.
#  6. NOTE: **VERY IMPORTANT** do not set any global variable(except *RUN_IN_STUDIO*)

RUN_IN_STUDIO=True

try:
    import Studio
    from Studio.project import *
    import sys, os, shutil, os.path
    import argparse
except:
    RUN_IN_STUDIO = False

import subprocess
import os
from os import path
import shutil
import stat
import sys
import platform

if platform.system() == "Windows":
    JENKINS_WORKSPACE="C:\\RBF\\jenkins_workspace\\"
    RUN_SCRIPT_PATH="C:\\SiliconLabs\\SimplicityStudio\\v5\\developer\\scripting\\runScript.bat"
else:
    ## TODO: Linux or Mac
    pass

HUB_CONFIG="HUB_DEVICE_A020"
RFM_CONFIG="RFM_DEVICE_A020"
HUB_PREFIX="HUB_MODULE"
DEVICE_TYPE_HEADER="device_type.h"

BUILD_ARG="BUILD_ARG.txt"
BUILD_NAME="BUILD_NAME.txt"
BUILD_ERROR="BUILD_ERROR.txt"

SUB_DEVICE_LIST = (
    ("SM_SOUND", "RBAD-SI1-868", "室内警号"),
    ("SM_OUTSOUND", "RBAD-SO1-868", "室外警号"),
    ("SM_MC", "RBSS-MC1-868", "门磁"),
    ("SM_EB", "RBSS-EB1-868","紧急按钮" ),
    ("SM_KF", "RBCT-KF1-868", "遥控器"),
    ("SM_RL", "RBAD-RL1-868", "弱电继电器"),
    ("SM_WS", "RBAD-WS1-868", "强电墙装开关"),
    ("SM_KP", "RBCT-KP1-868", "键盘"),
    ("SM_PIR","RBSS-PS1-868", "PIR探测器"),
    ("SM_WL",  "RBSS-WL1-868", "水浸"),
    ("SM_SMART_PLUG_EU", "RBAD-PL1-868-EU", "智能插座EU" ),
    ("SM_SMART_PLUG_US", "RBAD-PL1-868-US", "智能插座US" ),
    ("SM_SMART_PLUG_UK", "RBAD-PL1-868-UK", "智能插座UK" ),
    ("SM_SMART_PLUG_ITA", "RBAD-PL1-868-ITA", "智能插座ITA" ),
    ("SM_TEMPHUMI", "RBSS-TH1-868", "温湿度传感器"),
)

def read_file(path):
    pass

def del_rw(action, name, exc):
    os.chmod(name, stat.S_IWRITE)
    os.remove(name)

def rmtree(path):
    try:
        shutil.rmtree(path, onerror=del_rw)
    except:
        pass
def rpt_err(err_msg):
    print(err_msg)
    f = open(BUILD_ERROR, "w")
    f.write(err_msg + "\n")
    f.close()
    if not RUN_IN_STUDIO:
        exit(-1)
    else:
        open("none_exists_file_dusun_open_to_trigger_exception.txt", "r")

def get_build_name():
    try:
        f = open(BUILD_NAME)
        build_name = f.read().strip()
        return build_name
    except:
        return "NonBuildName"

def run_command(cmd):
    p = subprocess.run(cmd, text=True, stdout=subprocess.PIPE)

    if p.returncode == 0:
        return (True, p.stdout)
    else:
        return (False, None)

#获取协议栈版本 VX_X_X
def get_rbf_ver():
    rbf_ver = "V"
    f = open("./mac/RBFVersion.h", "r")
    lines = f.readlines()
    for line in lines:
        if line.startswith("#define MAJOR_VERSION"):
            end = line.find('//') 
            rbf_ver = rbf_ver + line[0:end].replace("#define","").replace("MAJOR_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
        if line.startswith("#define MINOR_VERSION"):
            end = line.find('//') 
            rbf_ver = rbf_ver + '.' + line[0:end].replace("#define","").replace("MINOR_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
        if line.startswith("#define STAGE_VERSION"):
            end = line.find('//') 
            rbf_ver = rbf_ver + '.' + line[0:end].replace("#define","").replace("STAGE_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
    print("rbf_ver",rbf_ver)
    return rbf_ver


#获取子设备版本 VX_X_X
def get_rfm_ver(prj_marco):
    rfm_ver = ""
    path = "./DS_app/singleMicroDevice/"

    if(prj_marco == "SM_SOUND"):
        path = path + "SM_sounder.c"
    elif(prj_marco == "SM_OUTSOUND"):
        path = path + "SM_outsounder.c"
    elif(prj_marco ==("SM_MC")):
        path = path + "SM_MC.c"
    elif(prj_marco ==("SM_EB")):
        path = path + "SM_EB.c"
    elif(prj_marco ==("SM_KF")):
        path = path + "SM_keyfob.c"
    elif(prj_marco ==("SM_RL")):
        path = path + "SM_RL.c"
    elif(prj_marco ==("SM_WS")):
        path = path + "SM_WS.c"
    elif(prj_marco ==("SM_KP")):
        path = path + "SM_keypad.c"
    elif(prj_marco ==("SM_PIR")):
        path = path + "SM_PIR.c"
    elif(prj_marco ==("SM_WL")):
        path = path + "SM_waterLeak.c"
    elif(prj_marco ==("SM_SMART_PLUG_EU")):
        path = path + "SM_smartPlug.c"
    elif(prj_marco ==("SM_SMART_PLUG_US")):
        path = path + "SM_smartPlug.c"
    elif(prj_marco ==("SM_SMART_PLUG_UK")):
        path = path + "SM_smartPlug.c"
    elif(prj_marco ==("SM_SMART_PLUG_ITA")):
        path = path + "SM_smartPlug.c"
    elif(prj_marco ==("SM_TEMPHUMI")):
        path = path + "SM_TempHumi.c"
    print(path)

    f = open(path, "r")
    lines = f.readlines()
    for line in lines:
        if line.startswith("#define APP_MAJOR_VERSION"):
            end = line.find('//') 
            rfm_ver = rfm_ver + line[0:end].replace("#define","").replace("APP_MAJOR_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
        if line.startswith("#define APP_MINOR_VERSION"):
            end = line.find('//') 
            rfm_ver = rfm_ver + '.' + line[0:end].replace("#define","").replace("APP_MINOR_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
        if line.startswith("#define APP_STAGE_VERSION"):
            end = line.find('//') 
            rfm_ver = rfm_ver + '.' + line[0:end].replace("#define","").replace("APP_STAGE_VERSION","").replace("(","").replace(")","").replace(" ","").replace("\n","")
    print("rfm_ver",rfm_ver)
    return rfm_ver

def save_hub_binary():
    prj_dir = os.getcwd()
    rbf_ver = get_rbf_ver()
    build_name = rbf_ver + "_" + get_build_name()
    hub_output = os.path.join(prj_dir, HUB_CONFIG)
    print("save hub binary " + prj_dir)
    #copy axf
    shutil.copyfile(path.join(hub_output, HUB_CONFIG + ".axf"),
            path.join("output", HUB_PREFIX + "_"+ build_name + ".axf"))

    # copy .gbl
    shutil.copyfile(path.join(hub_output, HUB_CONFIG + ".gbl"),
            path.join("output", HUB_PREFIX + "_E1_" + build_name + ".gbl"))
    # copy BOOT.hex
    shutil.copyfile(path.join(hub_output, HUB_CONFIG + "_BOOT.hex"),
            path.join("output", HUB_PREFIX + "_E1_" + build_name + ".hex"))


def save_dev_binary(prj_name,prj_marco):
    prj_dir = os.getcwd()
    build_name = get_build_name()
    rbf_ver = get_rbf_ver()
    rfm_ver = get_rfm_ver(prj_marco)
    build_name = rbf_ver + "_" + rfm_ver + "_" + build_name
    dev_output = path.join(prj_dir, RFM_CONFIG)

    rf_freq = get_rf_freq()
    prj_name = prj_name.replace("868", rf_freq)
    #copy axf
    shutil.copyfile(path.join(dev_output, RFM_CONFIG + ".axf"),
               path.join("output", prj_name + "_" + build_name + ".axf"))

    # copy .gbl
    shutil.copyfile(path.join(dev_output, RFM_CONFIG + ".gbl"),
               path.join("output", prj_name + "_" + build_name + ".gbl"))

    # shutil.copyfile(path.join(dev_output, RFM_CONFIG + "_OTA" + ".gbl"),
               # path.join("output", prj_name + "_" + build_name + "_OTA" + ".gbl"))

    shutil.copyfile(path.join(dev_output, RFM_CONFIG + "_LZMA_OTA" + ".gbl"),
               path.join("output", prj_name + "_" + build_name + "_LZMA_OTA" + ".gbl"))

    # copy BOOT.hex
    # flash 256K
    # shutil.copyfile(path.join(dev_output, RFM_CONFIG + "_BOOT_F256.hex"),
               # path.join("output", prj_name + "_E0_" + build_name + ".hex"))
    # flash 512K
    shutil.copyfile(path.join(dev_output, RFM_CONFIG + "_BOOT_F512.hex"),
               path.join("output", prj_name + "_E1_" + build_name + ".hex"))

def config_device_type(prj_marco):
    f = open(DEVICE_TYPE_HEADER, "r")
    lines = f.readlines()
    f.close()
    # print(lines)

    f = open(DEVICE_TYPE_HEADER, "w")

    marco_written = False
    rfm_device_opened = False

    for line in lines:
        if line.startswith("#ifdef RFM_DEVICE"):
            rfm_device_opened = True
        if rfm_device_opened and line.startswith("#endif"):
            rfm_device_opened = False

        if rfm_device_opened:
            if not line.startswith("#define SM_") or line.startswith("#define SM_HUB"):
                f.write(line)
        else:
            f.write(line)
        if line.startswith("#ifdef RFM_DEVICE") and not marco_written:
            marco_written = True
            f.write("#define " + prj_marco + "\n")

def build_hub():
    print("building HUB")
    build(HUB_CONFIG)
    save_hub_binary()

def build_sub_devices():
    for dev in SUB_DEVICE_LIST:
        print("start build:  " + dev[1] +" " + dev[0])
        build_sub_dev(dev[1], dev[0])

def build_sub_dev(prj_name, prj_marco):
    print(prj_name, prj_marco)
    config_device_type(prj_marco)
    build(RFM_CONFIG)
    save_dev_binary(prj_name,prj_marco)


def build_all():
    build_hub()
    build_sub_devices()

def get_rf_freq():
    f = open("config.h", "r")
    lines = f.readlines()
    f.close()
    for line in lines:
        line = line.strip()
        if line.startswith("//") or line.startswith("/*"):
            continue
        if not line.startswith("#define"):
            continue
        if line.find("CONFIG_RF_FREQ_") < 0:
            continue
        line = line.replace("#define", "")
        line = line.replace("CONFIG_RF_FREQ_", "")
        line = line.strip()
        return line
    return "UnknownFreq"

def parse_args():
    # if there are command line args, convert these into jenkins format
    # parse build_name, first arg
    if len(sys.argv) > 1:
        f = open(BUILD_NAME, "w")
        f.write(sys.argv[1].strip())
        f.close()

    if len(sys.argv) >= 3:
        f = open(BUILD_ARG, "w")
        for i in range(2, len(sys.argv)):
            marco = sys.argv[i].strip().upper()
            if marco == "ALL":
                f.write(marco + "\n")
                break
            if marco.find("SM_") <0:
                marco = "SM_" +  marco
            f.write(marco + "\n")
        f.close()
    else:
        if not os.path.isfile(BUILD_ARG):
            print("Default BUILD_ARG to ALL")
            f = open(BUILD_ARG, "w")
            f.write("ALL\n")
            f.close()

def normalize_build_arg():
    # remove comment in BUILD_ARG.txt
    f = open(BUILD_ARG, "r")
    lines = f.readlines()
    f.close()
    f = open(BUILD_ARG, "w")

    for line in lines:
        line = line.strip()
        if line.startswith("#") or line.startswith("//"):
            continue
        idx = line.find("//")
        if idx > 0:
            line = line[0:idx]
        idx = line.find("#")
        if idx >0:
            line = line[0:idx]
        line = line.strip()

        if len(line) == 0:
            continue
        f.write(line + "\n")
    f.close()

def prep_env():
    print("prepare/check build enviroment")
    try:
        shutil.rmtree("output")
    except:
        pass

    try:
        os.remove(BUILD_ERROR)
    except:
        pass

    os.mkdir("output") ## 用于放置生成的固件
    if not "PATH_GCCARM" in os.environ or not "PATH_SCMD" in os.environ:
        rpt_err("PATH_GCCARM or PATH_SCMD not set")

    # path = os.environ["Path"]
    # if path.find("adapter\\commander") < 0:
    #     rpt_err("commander not in path")
    if not os.path.isfile(RUN_SCRIPT_PATH):
        rpt_err("can find runScript, please check if Simplicity Studio v5 is installed")

def get_prj_name_from_marco(prj_marco):
    for dev in SUB_DEVICE_LIST:
        if dev[0] == prj_marco:
            return dev[1]
    return ""

def build_with_jenkins_args():
    print("build with arguments from Jenkins")
    f = open(BUILD_ARG, "r")
    marcos = f.readlines()
    if len(marcos) > 0 and marcos[0].strip() == "ALL":
        build_all()
        return

    for marco in marcos:
        prj_marco = marco.strip()
        if prj_marco == "SM_HUB":
            build_hub()
            continue
        prj_name = get_prj_name_from_marco(prj_marco)
        if prj_name == "":
            rpt_err("config does not exist " + marco)
        build_sub_dev(prj_name, prj_marco)

def build(config):
    rmtree(RFM_CONFIG)
    rmtree(HUB_CONFIG)

    fileName = os.path.basename(os.getcwd())
    baseName = os.path.splitext(fileName)[0]

    project = Studio.project.getProject(baseName)
    buildOptions = {
        OPTION_BUILD_TYPE: BUILD_TYPE_FULL,
        OPTION_BUILD_CONFIG: config,
        OPTION_NO_AUTO_ENABLE : True
    }

    results = Studio.project.buildProject(project, buildOptions, None)
    if results.anyErrors():
        rpt_err("Studio build error")

def studio_main():
    ## main start when run in studio envoriment
    fileName = os.path.basename(os.getcwd())
    baseName = os.path.splitext(fileName)[0]
    importOptions = {
        OPTION_PROJECT_NAME : baseName,
        OPTION_NO_AUTO_ENABLE : True
    }

    project = Studio.project.getProject(baseName)
    if project.exists():
        print("*** Deleting existing project",project.name)
        project.delete(project.NEVER_DELETE_PROJECT_CONTENT, None)
    else:
        print("*** project do not exists in workspace, no need to delete project")

    path = os.getcwd()
    print("Import current dir: " + path + " into workspace")
    project = Studio.project.importExistingProject(
                path,
                importOptions,
                None)
    print("Import done")

    build_with_jenkins_args()

def main():

    print("run in studio :  " + str(RUN_IN_STUDIO))

    if RUN_IN_STUDIO:
        print("Now is running in Simplicity Studio")
        studio_main()
    else:
        parse_args()
        normalize_build_arg()
        prep_env()
        print("Starting Simplicity Studio....")
        subprocess.run([RUN_SCRIPT_PATH, "-data", JENKINS_WORKSPACE,
                  "build.py", "-cfg", "", os.getcwd()] )

main()
